home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994…tember: Reference Library / Dev.CD Sep 94.toast / Periodicals / develop / develop Issue 9 / develop 9 code / Tracks / drvr.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-11-16  |  38.4 KB  |  1,469 lines  |  [TEXT/MPS ]

  1. #include "TraceModule.h"
  2. #include <ToolUtils.h>
  3. #include <Errors.h>
  4. #include <Files.h>
  5. #include <Memory.h>>
  6. #include <Strings.h>
  7. #include <OSUtils.h>
  8. #include <Events.h>
  9. #include <Resources.h>
  10. #include <Devices.h>
  11. #include <types.h>     // Mac types
  12. #include <Desk.h>    /* for accRun */
  13. /***********************************************************************
  14. //
  15. // Tracks Driver
  16. // Copyright 1990, 1991 Orion Network Systems, Inc. All Rights Reserved.
  17. // Authors :  Jim Flood, Brad Lowe
  18. //
  19. ************************************************************************/
  20.  
  21.  
  22. // clears circular buffer by setting start and end to the same place
  23. snum ClearTraceBuffer(TraceGlobals *globals)
  24. {
  25. register short     result;
  26.  
  27.     if ( ! globals->fBufferEnabled)
  28.         result = fnOpnErr; // "File not open" means trace is not enabled
  29.     else
  30.     {
  31.         result = noErr;
  32.         //    when nextRead == nextWrite the buffer is empty..
  33.         //     Make it so
  34.         globals->fNextReadPtr = globals->fBuffStartPtr;
  35.         globals->fNextWritePtr = globals->fBuffStartPtr;    
  36.     }
  37.     return(result);
  38. }
  39.  
  40. // Each write to the file opens, writes and closes the file.  This function
  41. // closes the output file.
  42. snum CloseTraceFile(TraceGlobals *globals)
  43. {
  44. register    snum result;
  45.     if ( ! globals->fFileIsOpen)
  46.         result = fnOpnErr; // "File not open" Error -38
  47.     else
  48.         {
  49.         // Close the file and flush the volume.
  50.         // Set fFileIsOpen to false 
  51.  
  52.  
  53.         result = FSClose(globals->fFileRefNum);
  54.         if (result == noErr)    /* This would be is very odd if !true... */
  55.             {
  56.             globals->fFileIsOpen = false;
  57.             result = FlushVol(nil, globals->fVRefNum);
  58.             }
  59.         // (void) SetFLock(globals->fTraceFileName, globals->fVRefNum);
  60.         }
  61.     return(result);
  62.  
  63. }
  64.  
  65. // Copy a mask.  A mask would be either a break or a TracePoints mask.
  66. void DoCopyMask(MaskType src,MaskType dst)
  67. {
  68.     BlockMove((Ptr)src,(Ptr)dst,sizeof(MaskType));
  69. }
  70.  
  71.  
  72. snum DisableTraceBuffer(TraceGlobals *globals)
  73.     {
  74.     register short result;
  75.  
  76.     if ( ! globals->fBufferEnabled)
  77.         result = fnOpnErr; // "File not open" means trace is not enabled
  78.     else
  79.         {
  80.         result = noErr;
  81.         globals->fBufferEnabled = false;
  82.         DisposPtr(globals->fBuffStartPtr);
  83.         }
  84.         
  85.     return(result);
  86.     
  87.     }
  88.  
  89. snum DisableTraceFile(TraceGlobals *globals)
  90.     {
  91.     register snum result;
  92.     
  93.     if ( ! globals->fFileEnabled)
  94.         result = volOffLinErr; // "volume offline" means already disabled
  95.     else
  96.         {
  97.             globals->fFileEnabled = false;
  98.             // (void) RstFLock(globals->fTraceFileName, globals->fVRefNum);
  99.             result = noErr;
  100.         }
  101.         
  102.     return(result);
  103.     
  104.     }
  105.  
  106. snum EnableTraceBuffer(TraceGlobals *globals, TraceParamBlock *paramPtr)
  107. {
  108. register short result;
  109. register Ptr bufferPtr;
  110. register lhex traceBuffSize;
  111. register shex maxRecordSize;
  112.  
  113.     if (globals->fBufferEnabled)
  114.         result = fBsyErr; // "File is busy" means trace already enabled -47
  115.     else
  116.         {
  117.         // Make sure the buffer size is even:
  118.     
  119.         traceBuffSize = paramPtr->u.enable.traceBuffSize;
  120.         if (traceBuffSize & 1)
  121.             traceBuffSize--;
  122.         
  123.         // Allocate the trace buffer (on the System Heap)
  124.  
  125.         bufferPtr = NewPtrSys(traceBuffSize);
  126.  
  127.         if (bufferPtr == nil)
  128.             {
  129.             result = MemError();
  130.             }
  131.             
  132.         else
  133.             {
  134.             
  135.             result = noErr;
  136.             
  137.             globals->fBufferEnabled = true;
  138.             globals->fLockedOutFlag = false;    // reset locked-out flag
  139.                         
  140.             // initialize buffer control fields :
  141.     
  142.             globals->fTraceBuffSize = traceBuffSize;
  143.             globals->fTraceBuffSizeIndex = paramPtr->u.enable.traceBuffSizeIndex;
  144.             globals->fBuffStartPtr = bufferPtr;
  145.             globals->fBuffEndPtr = bufferPtr + (traceBuffSize - 1);
  146.             globals->fNextReadPtr = bufferPtr;
  147.             globals->fNextWritePtr = bufferPtr;
  148.     
  149.             // Maximum size data record must leave room for at least one
  150.             // Lost-Data record and one extra free byte in the buffer
  151.     
  152.             maxRecordSize = (traceBuffSize - sizeof(NewRecordTemplate)) - 1;
  153.             globals->fMaxRecordSize = maxRecordSize;
  154.  
  155.             }
  156.         }
  157.         
  158.     return(result);
  159.     
  160.     }
  161. snum SetTraceFileName(TraceGlobals *globals, TraceParamBlock *paramPtr)
  162. {
  163. register snum     result;
  164. register Ptr     fileName;
  165. register chex     strlen; // unsigned!
  166. FInfo     finderInfo;
  167. long     fileLength = 0;
  168.  
  169. //saveTypeHandle    hSave;
  170. //short    PrefsResRefNum;
  171.  
  172.     (void) CloseTraceFile(globals);    /* Close out any old file...  */
  173.     // (void) RstFLock(globals->fTraceFileName,globals->fVRefNum);    /* And let them trash it */
  174.     fileName = (Ptr) paramPtr->u.enableFile.fileName;
  175.     result = noErr;
  176.     
  177.     if (globals->fFileEnabled && 0 )        //  NO LONGER APPLIES 
  178.         result = volOnLinErr; // "volume already online" means already enabled
  179.         
  180.     else if (fileName == nil)
  181.         result = bdNamErr; // "Bad file name"
  182.     else
  183.         {
  184.  
  185.         // Volume refnum is passed also:
  186.         
  187.         globals->fVRefNum = paramPtr->u.enableFile.vRefNum;
  188.         
  189.         // (Truncate file name if too long)
  190.         
  191.         strlen = *fileName + 1;
  192.         if (strlen > sizeof(globals->fTraceFileName))
  193.             strlen = sizeof(globals->fTraceFileName);
  194.         
  195.         // Copy the file name:
  196.  
  197.         BlockMove(fileName, globals->fTraceFileName, strlen);
  198.         globals->fTraceFileName[0] = strlen - 1; // (in case we truncated it)
  199.         
  200.         // Check if the file exists, and if it has the right Finder info..
  201.         // If it doesn't exist, then create it.
  202.         
  203.         result = GetFInfo(globals->fTraceFileName, globals->fVRefNum, &finderInfo);
  204.  
  205.         if (result == fnfErr)    // Doesnt exist, create a new one... 
  206.             {
  207.                 result = Create(fileName, globals->fVRefNum, globals->fMyCreator, globals->fMyFileType);
  208.             }
  209.         else 
  210.         if (result == noErr)
  211.             {
  212.             // Check file type and creator (it must be ours):
  213.             if ( (finderInfo.fdType != globals->fMyFileType) ||
  214.                  (finderInfo.fdCreator != globals->fMyCreator) )
  215.                 result = extFSErr; // "External file system"
  216.             }
  217.         }
  218.     
  219.     if (result == noErr)
  220.         {
  221.             (void) OpenTraceFile(globals);
  222.             if (result == noErr)
  223.                 {
  224.                     (void) GetEOF(globals->fFileRefNum,&fileLength);
  225.                 }
  226.             (void)CloseTraceFile(globals);
  227.             globals->fFileEnabled = true;
  228.             globals->fBytesWritten = fileLength;
  229.             // (void) SetFLock(globals->fTraceFileName,globals->fVRefNum);    // no trash
  230.         }
  231.     return(result);
  232. }
  233.  
  234.  
  235. snum GetBufferSpace(TraceGlobals *globals, shex amountOfSpace, Boolean *dataLost)
  236.     {
  237.     snum result;
  238.     register shex recordSize;
  239.     register lhex freeSpace;
  240.     register Boolean dataWasLost;
  241.     register lhex bufferSize;
  242.     register Ptr nextRead;
  243.     register Ptr nextWrite;
  244.     register Ptr buffEnd;
  245.     
  246.     // This function attempts to clear the requested amount of space
  247.     // in the circular buffer, throwing out older records if needed.
  248.     // If data is thrown out, true is returned in 'Boolean *dataLost'.
  249.     // If the amount requested exceeds the maximum allowed record size,
  250.     // then the function returns -1 without touching the buffer.
  251.     // (Of course if there is plenty of room available, then no older
  252.     // records are lost.)
  253.     
  254.     if (amountOfSpace > globals->fMaxRecordSize)
  255.         result = -1;
  256.     else
  257.         {
  258.         result = 0;
  259.         dataWasLost = false;
  260.         
  261.         // Move some values into register variables for optimization:
  262.         
  263.         nextRead = globals->fNextReadPtr;
  264.         nextWrite = globals->fNextWritePtr;
  265.         buffEnd = globals->fBuffEndPtr;
  266.         bufferSize = globals->fTraceBuffSize;
  267.         
  268.         // Loop until we have our space...
  269.         
  270.         freeSpace = 0;
  271.         while (amountOfSpace >= freeSpace)
  272.             {
  273.  
  274.             // Calculate how much free space is in the buffer.  If the read and write
  275.             // pointers are equal, then the buffer is empty.  If the write pointer is
  276.             // lower than the read pointer, then the free space is between the pointers
  277.             // (i.e. up until the read pointer is reached).  If the write pointer is
  278.             // above the read pointer, then the free space is from the write pointer to
  279.             // the end of the buffer, then wrapping around from the start of the buffer
  280.             // up to the read pointer (i.e. the space between the pointers contains
  281.             // unread trace records).
  282.  
  283.             if (nextWrite == nextRead)                 // READ==WRITE means empty buffer
  284.                 freeSpace = bufferSize;
  285.             else if (nextWrite < nextRead)
  286.                 freeSpace = nextRead - nextWrite;
  287.             else
  288.                 freeSpace = bufferSize - (nextWrite - nextRead);
  289.  
  290.             // If not enough free space, toss the oldest record and try again:
  291.             
  292.             if (amountOfSpace >= freeSpace)
  293.                 {
  294.                 
  295.                 dataWasLost = true;
  296.  
  297.                 // retrieve the LL field :
  298.                 // (2 byte length field always falls on even memory boundary)
  299.                 
  300.                 recordSize = *((shex *) nextRead);
  301.  
  302.                 // Move the read pointer, wrapping to start of buffer is needed:
  303.                 
  304.                 nextRead += recordSize;
  305.                 
  306.                 if (nextRead > buffEnd)
  307.                     nextRead = globals->fBuffStartPtr + (nextRead - buffEnd) - 1;
  308.  
  309.                 }
  310.             }
  311.         
  312.         // Move register variable value back into globals in case it was modified:
  313.         
  314.         globals->fNextReadPtr = nextRead;
  315.         
  316.         // Return lost data indication:
  317.         
  318.         *dataLost = dataWasLost;
  319.         
  320.         }
  321.     
  322.     return(result);
  323.     
  324. }
  325.  
  326.  
  327. /*
  328. **    Set our gloabal data masks to the new mask that we get from the CDEV... 
  329. */
  330.  
  331. snum GetMaskFromCDEV(TraceGlobals *globals, TraceParamBlock *paramPtr,short csCode)
  332. {
  333.     if (csCode == kGetCdevBreakMask)        
  334.         DoCopyMask(paramPtr->u.mask.Mask,globals->fBreakMask);
  335.     else    // kGetCdevTraceMask by default
  336.         DoCopyMask(paramPtr->u.mask.Mask,globals->fTraceMask);
  337.     globals->fBreakOnceThenClear = paramPtr->u.mask.BreakOnce;
  338.     return(noErr);
  339. }
  340.  
  341.  
  342. void GetNewRecord(TraceGlobals *globals, shex recordSize, Ptr *recordAddr,
  343.  shex *splitSize, Ptr *secondAddr)
  344.     {
  345.     snum result;
  346.     Boolean dataWasLost;
  347.     NewRecordTemplate template;            // Used to create a lost-data record
  348.     register shex topSize;
  349.     register shex secondPart;
  350.     register Ptr buffStart;
  351.     register Ptr buffEnd;
  352.     register Ptr nextWrite;
  353.     register Ptr nextRead;
  354.     
  355.     dataWasLost = false;
  356.     
  357.     // Get space for the new record:
  358.     // To increase performance, always ask for enough room for the new record
  359.     // plus a lost-data record.  Although the extra room for the lost-data record
  360.     // may be what causes a lost-data condition, in this case the buffer is almost
  361.     // full anyway and we are bound to lose data soon anyway.  In this implementation,
  362.     // we will sacrifice the efficiency of the buffer size in favor of efficiency
  363.     // of execution (after all, if we are letting the buffer run full without
  364.     // retrieving any records, we will definitely want the efficiency).
  365.     // NOTE: Currently, if GetBufferSpace returns an error, we don't do anything at all.
  366.     
  367.     result = GetBufferSpace(globals, recordSize + sizeof(NewRecordTemplate), &dataWasLost);
  368.     
  369.     if (result == 0)
  370.         {
  371.         
  372.         // Move some values into register variables for optimization:
  373.         
  374.         nextWrite = globals->fNextWritePtr;
  375.         nextRead = globals->fNextReadPtr;
  376.         buffStart = globals->fBuffStartPtr;
  377.         buffEnd = globals->fBuffEndPtr;
  378.     
  379.         // If data was lost while finding buffer space, then insert a lost-data record:
  380.         // This record must be inserted as the next read (in front of all other records).
  381.         
  382.         if (dataWasLost)
  383.             {
  384.             template.length = sizeof(NewRecordTemplate);
  385.             template.diagID = kLostDataRecord;
  386.             template.partCode = 0;
  387.             if (globals->fTimeStampType == 1)
  388.                 GetDateTime(&template.timeStamp); // Date and Time timestamp
  389.             else
  390.                 template.timeStamp = TickCount(); // Ticks (1/60 sec) timestamp
  391.             template.timeStampType = globals->fTimeStampType;
  392.             template.formatID = 0x00;
  393.  
  394.             secondPart = nextRead - buffStart;
  395.             if (secondPart >= sizeof(NewRecordTemplate))
  396.                 {
  397.                 // It will fit in one piece ahead of next read record:
  398.                 nextRead -= sizeof(NewRecordTemplate);
  399.                 BlockMove((Ptr) &template, nextRead, sizeof(NewRecordTemplate));
  400.                 }
  401.             else
  402.                 {
  403.                 // The record must be split (wrap-around backwards):
  404.                 topSize = sizeof(NewRecordTemplate) - secondPart;
  405.                 nextRead = (buffEnd - topSize) + 1;
  406.                 BlockMove((Ptr) &template, nextRead, topSize);
  407.                 BlockMove(((Ptr) &template) + secondPart, buffStart, secondPart);
  408.                 }
  409.  
  410.             // Move the register copy back into the globals block:
  411.         
  412.             globals->fNextReadPtr = nextRead;
  413.             
  414.             }
  415.         
  416.         // Calculate the return values:
  417.         
  418.         *recordAddr = nextWrite;            // this is where the new record goes
  419.         
  420.         topSize = (buffEnd - nextWrite) + 1;
  421.         if (topSize >= recordSize)
  422.             {
  423.             *splitSize = recordSize;        // If we can fit it all before reaching the end...
  424.             *secondAddr = nil;                // ... then there is no second part to the record
  425.             secondPart = 0;
  426.             }
  427.         else
  428.             {
  429.             secondPart = recordSize - topSize;
  430.             *splitSize = topSize;            // If the record passes the end of the buffer...
  431.             *secondAddr = buffStart;        // ... then pass the address for the second part
  432.             }
  433.         
  434.         // Fix up the next write pointer:
  435.         
  436.         nextWrite += recordSize;
  437.         if (nextWrite > buffEnd)
  438.             nextWrite = buffStart + secondPart;
  439.         
  440.         // Don't forget to move the register copy back into the globals block:
  441.         
  442.         globals->fNextWritePtr = nextWrite;
  443.         
  444.         }
  445.     
  446.     return;
  447.     
  448.     }
  449. snum GetTraceProc(TraceGlobals *globals, TraceParamBlock *paramPtr)
  450. {
  451.  
  452.     paramPtr->u.traceProc.traceProcPtr = (TraceProcPtr)TraceProc;
  453.     paramPtr->u.traceProc.traceValue = (long) globals;
  454.  
  455.     return(noErr);
  456. }
  457.  
  458. snum GetTraceStatus(TraceGlobals *globals, TraceParamBlock *paramPtr)
  459.     {
  460.     TraceStatusBlk *statusPtr;
  461.     Ptr nextRead;
  462.     Ptr nextWrite;
  463.     long bytesBuffered;
  464.     char *s, *t;
  465.     short len;
  466.     short i;
  467.     
  468.     statusPtr = paramPtr->u.getStatus.statusPtr;
  469.     
  470.     statusPtr->online = globals->fTraceOnline;
  471.     statusPtr->bufferEnabled = globals->fBufferEnabled;
  472.     statusPtr->fileEnabled = globals->fFileEnabled;
  473.     statusPtr->autoWrite = globals->fAutomaticWrite;
  474.  
  475.     statusPtr->bufferSize = globals->fTraceBuffSize;
  476.     statusPtr->bufferSizeIndex = globals->fTraceBuffSizeIndex;
  477.     statusPtr->breakOnceThenClear = globals->fBreakOnceThenClear; 
  478.     statusPtr->DebugMarkUnset = globals->fDebugMarkUnset;
  479.     statusPtr->TraceOnStartup = globals->fTraceOnStartup;
  480.     if ( ! globals->fBufferEnabled)
  481.         bytesBuffered = 0;
  482.     else
  483.         {
  484.         nextRead = globals->fNextReadPtr;
  485.         nextWrite = globals->fNextWritePtr;
  486.         if (nextRead < nextWrite)
  487.             bytesBuffered = nextWrite - nextRead;
  488.         else if (nextRead > nextWrite)
  489.             bytesBuffered = globals->fTraceBuffSize - (nextRead - nextWrite);
  490.         else
  491.             bytesBuffered = 0;
  492.         }
  493.     statusPtr->bytesBuffered = bytesBuffered;
  494.         
  495.     if ( ! globals->fFileEnabled)
  496.         {
  497.         statusPtr->bytesWritten = 0;
  498.         statusPtr->fileVolume = 0;
  499.         t = statusPtr->fileName;
  500.         for (i = 0; i < sizeof(statusPtr->fileName); i++)
  501.             *t++ = 0;
  502.         }
  503.     else
  504.         {
  505.         statusPtr->bytesWritten = globals->fBytesWritten;
  506.         statusPtr->fileVolume = globals->fVRefNum;
  507.         len = globals->fTraceFileName[0] + 1;
  508.         s = globals->fTraceFileName;
  509.         t = statusPtr->fileName;
  510.         for (i = 0; i < len; i++)
  511.             *t++ = *s++;        
  512.         for (i = len; i < sizeof(statusPtr->fileName); i++)
  513.             *t++ = 0;
  514.         }
  515.         
  516.     return(noErr);
  517.     
  518.     }
  519.  
  520. /* 
  521.     This is called once by DRVR open...  It inits the globals, and finds out the 
  522.     driver name that Trace will trace.
  523. */
  524.  
  525. void InitTrace(TraceGlobals *globals)
  526. {
  527.     globals->fCheckID = 'TRAC';
  528.     globals->fMyCreator = 'BNNY';
  529.     globals->fMyFileType = kPrefsOSType;    // preference file type 
  530.  
  531.     globals->fBuffStartPtr = nil;
  532.     globals->fBuffEndPtr = nil;        
  533.     globals->fNextReadPtr = nil;
  534.     globals->fNextWritePtr = nil;
  535.  
  536.     globals->fTraceLock = false;
  537.     globals->fBufferEnabled = false;
  538.     globals->fFileEnabled = false;
  539.     globals->fTraceOnline = false;
  540.     globals->fFileIsOpen = false;
  541.     globals->fAutomaticWrite = false;
  542.     globals->fLockedOutFlag = false;
  543.     
  544.     globals->fBytesWritten = 0;
  545.     globals->fWriteErr = 0;
  546.  
  547.     globals->fDriversRefNum = 0;
  548.     
  549.     globals->fTimeStampType = 1; // Always use date/time for now
  550.     globals->fBreakOnceThenClear = true;
  551.  
  552.     return;    
  553. }
  554.  
  555. snum OpenTraceFile(TraceGlobals *globals)
  556.     {
  557.     register snum result;
  558.     register char *fileName;
  559.     register short vRefNum;
  560.  
  561.     if (globals->fFileIsOpen)
  562.         result = tmfoErr; // "too many files open" if a file is already open
  563.     else
  564.         {
  565.     //    (void) RstFLock(globals->fTraceFileName,globals->fVRefNum);
  566.  
  567.         fileName = globals->fTraceFileName;
  568.         vRefNum = globals->fVRefNum;
  569.         
  570.         // Open the file - return an error if file is not found
  571.         // (EnableTraceFile should have already created it)
  572.         
  573.         result = FSOpen(fileName, vRefNum, &(globals->fFileRefNum));
  574.             
  575.         // Set the file position to the end of the file.
  576.         
  577.         if (result == noErr)
  578.             {
  579.                 (void) SetFPos(globals->fFileRefNum, fsFromLEOF, 0);
  580.                 globals->fFileIsOpen = 1;
  581.             }
  582.         }
  583.         
  584.     return(result);
  585.     
  586.     }
  587.  
  588. /* 
  589. **    Effectivly erases the data in an output file,
  590. **    without all the hassles of using finder 
  591. */
  592. snum ResetEOF(TraceGlobals *globals)
  593. {
  594. register snum result;
  595.     // If the  trace file is enabled, then open the file,
  596.     // and set the eof...
  597.     
  598.     if ( ! globals->fFileEnabled && 0)        // Ignore this if for now.. 
  599.         result = volOffLinErr;         // "Volume not on-line"
  600.     else
  601.         {        
  602.             result = OpenTraceFile(globals);
  603.             if (result == noErr)
  604.                 {
  605.                     // Set the EOF to the start of the file.
  606.                     (void) SetEOF(globals->fFileRefNum,0L);        // ignore errors here
  607.                     (void) CloseTraceFile(globals);    // ignore errors here
  608.                     (void) FlushVol(nil, globals->fVRefNum);         // ignore errors here
  609.                     globals->fBytesWritten = 0L;
  610.                 }
  611.         }
  612.     return(result);
  613. }
  614.  
  615.  
  616. /**
  617. ***        Driver to CDEV 
  618. **/
  619.  
  620. snum SendMaskToCdev(TraceGlobals *globals, TraceParamBlock *paramPtr,short csCode)
  621. {
  622.  
  623.     if (csCode == kSendCdevBreakMask)        
  624.         DoCopyMask(globals->fBreakMask,paramPtr->u.mask.Mask);
  625.     else
  626.         if (csCode == kSendCdevTraceMask)        
  627.             DoCopyMask(globals->fTraceMask,paramPtr->u.mask.Mask);
  628.  
  629.     globals->fDebugMarkUnset = false;
  630.     paramPtr->u.mask.BreakOnce = globals->fBreakOnceThenClear;    // Breakpoint method
  631.     return(noErr);
  632. }
  633.  
  634. snum SetTraceOffline(TraceGlobals *globals)
  635.     {
  636.     snum result;
  637.     CntrlParam ctlPB;
  638.     
  639.     if ( ! globals->fTraceOnline)
  640.         result = volOffLinErr; // "volume not on-line"
  641.     else
  642.         {
  643.         
  644.         // Issue PBControl to the target driver:
  645.         
  646.         ctlPB.ioCompletion = 0;
  647.         ctlPB.ioCRefNum = globals->fDriversRefNum;
  648.         ctlPB.csCode = kRemoveTrace;
  649.     
  650.         result = PBControl((ParamBlockRec *) &ctlPB, false);    // not async
  651.     
  652.         if (result == noErr)
  653.             globals->fTraceOnline = false;
  654.         }
  655.     
  656.     return(result);
  657.     
  658.     }
  659.  
  660.  
  661. snum SetTraceOnline(TraceGlobals *globals, char *driverDotName)
  662. {
  663. snum result;
  664. CntrlParam ctlPB;
  665. short x;
  666. char lengthByte;
  667.  
  668.     if (globals->fTraceOnline)
  669.         result = volOnLinErr; // "volume already online"
  670.     else
  671.         {
  672.         
  673.         // Find the refnum of the correct driver:
  674.         
  675.         if (globals->fTraceBuffSize == 0) 
  676.             result = aspBufTooSmall;    // Buffer too small.  May be uninitialized
  677.         else
  678.             result = OpenDriver(driverDotName, &(globals->fDriversRefNum));
  679.         
  680.         if (result == noErr)
  681.             {
  682.             lengthByte = *driverDotName;
  683.             for (x=0;x<lengthByte + 1;x++)
  684.                 globals->fDriverPStrName[x] = *driverDotName++;
  685.             
  686.             // Issue PBControl to the target driver:
  687.             
  688.             ctlPB.ioCompletion = 0;
  689.             ctlPB.ioCRefNum = globals->fDriversRefNum;
  690.             ctlPB.csCode = kInstallTrace;
  691.             *((ProcPtr *) &(ctlPB.csParam[0])) = (ProcPtr) StripAddress((Ptr) TraceProc);
  692.             *((Ptr *) &(ctlPB.csParam[2])) = (Ptr) globals;
  693.             
  694.             result = PBControl((ParamBlockRec *) &ctlPB, false);    // not async
  695.             
  696.             if (result == noErr)
  697.             {
  698.                 globals->fTraceOnline = true;
  699.             }
  700.     
  701.             } else 
  702.             {    
  703.                 globals->fDriverPStrName[0] = 0;
  704.             }
  705.         }
  706.     return(result);
  707. }
  708.  
  709. OSErr TClose(CntrlParam *ctlPB, DCtlPtr dCtl)
  710. {
  711. #pragma unused (ctlPB,dCtl)
  712.     return(closErr); // Not allowed to close this driver
  713. }
  714.  
  715.  
  716. OSErr TControl(CntrlParam *ctlPB, DCtlPtr dCtl)
  717.     {
  718.     register OSErr error;
  719.     register short csCode;
  720.     register TraceParamBlock *paramPtr;    
  721.     register TraceGlobals *globals;
  722.         
  723.     // The dCtlStorage field in the DCE entry contains our globals pointer
  724.  
  725.     globals = (TraceGlobals *) dCtl->dCtlStorage;
  726.     csCode = ctlPB->csCode;
  727.     
  728.     // change accRun to DoPeriodicTime
  729.     
  730.     if (csCode == accRun)
  731.         csCode = kDoPeriodicTime;
  732.  
  733.     // The TraceParamBlock is in csParam:
  734.  
  735.     paramPtr = (TraceParamBlock *) &(ctlPB->csParam);
  736.  
  737.     // Call the Tracks code.
  738.  
  739.     error = TraceEntry(globals, csCode, paramPtr, dCtl);
  740.         
  741.     return(error);
  742.     
  743.     }
  744.  
  745.  
  746. OSErr TOpen(CntrlParam *ctlPB, DCtlPtr dCtl)
  747.     {
  748. #pragma unused (ctlPB)
  749.     register OSErr error;
  750.     register TraceGlobals *globals;
  751.  
  752.     // The dCtlStorage field in the DCE entry is used to keep a pointer
  753.     // to the global memory block.  This will be nil on the first Open call.
  754.     
  755.     error = noErr;
  756.     globals = (TraceGlobals *) dCtl->dCtlStorage;
  757.     
  758.     if (globals == nil)
  759.         {
  760.         // Allocate global storage as a non-relocatable block on the System Heap...
  761.  
  762.           globals = (TraceGlobals *) NewPtrSysClear(sizeof(TraceGlobals));
  763.  
  764.         if (globals == nil)
  765.             error = MemError();
  766.         else
  767.             {
  768.  
  769.             // Initialize the trace code:
  770.             
  771.             InitTrace(globals);
  772.  
  773.             // Save a pointer to the globals in the DCE:
  774.         
  775.             dCtl->dCtlStorage = (Handle) globals;
  776.             
  777.             }
  778.         }
  779.  
  780.     return(error);
  781.     
  782.     }
  783.  
  784.  
  785. OSErr TPrime(CntrlParam *ctlPB, DCtlPtr dCtl)
  786.     {
  787. #pragma unused (ctlPB, dCtl)
  788.     return(readErr);
  789.     }
  790.  
  791. OSErr TStatus(CntrlParam *ctlPB, DCtlPtr dCtl) {
  792. register OSErr error;
  793. register short csCode;
  794. register TraceParamBlock *paramPtr;    
  795. register TraceGlobals *globals;
  796.         
  797.     // The dCtlStorage field in the DCE entry contains our globals pointer
  798.  
  799.     globals = (TraceGlobals *) dCtl->dCtlStorage;
  800.     csCode = ctlPB->csCode;
  801.     
  802.     // The TraceParamBlock is returned in csParam:
  803.  
  804.     paramPtr = (TraceParamBlock *) &(ctlPB->csParam);
  805.  
  806.     error = TraceEntry(globals, csCode, paramPtr, dCtl);
  807.         
  808.     return(error);
  809.     
  810. }
  811.  
  812.  
  813. void HandleTraceData(TraceGlobals *globals, char diagID, char partCode, 
  814.     char formatID, long data1, long data2, long data3);
  815.  
  816. BlockAppend(Ptr sourcePtr, SafeMemBlock *MemBlock, long requestSize)
  817. {
  818. register long    sizeLeft;
  819. register short result = 0;
  820.  
  821.     sizeLeft  = (long) (MemBlock->End - MemBlock->Mark);
  822.  
  823.     if (sizeLeft-10 < requestSize) 
  824.         {
  825.             requestSize = sizeLeft -1;
  826.             result = memSCErr;     // memory size check...
  827.         }
  828.  
  829.     BlockMove(sourcePtr,MemBlock->Mark, requestSize);
  830.     MemBlock->Mark += requestSize;
  831.     return result;
  832. }
  833.  
  834. /* This is the entry point for Trace calls from the target driver.
  835. ** This routine is not called from the driver-- it is called by the target driver.
  836. ** It can access this driver's globals because the target driver passes them in.
  837. */
  838. pascal void TraceProc(long refcon, char diagID, char partCode,
  839.     char formatID, long data1, long data2, long data3)
  840. {
  841. register TraceGlobals *globals;
  842. register Boolean     okLocked;
  843. register Boolean    BreakOnExit = false;    
  844.  
  845.     globals = (TraceGlobals *)refcon;    // set up driver globals
  846.  
  847.     if (diagID < 128)        // vaild diagID's range from 0 to 127... 
  848.         {    
  849.             // Check to see if there should be a break on exit (breakpoint was set).
  850.             BreakOnExit = BitTst((Ptr)globals->fBreakMask,(long)diagID);
  851.             
  852.             
  853.             //  Check to see the information passed should be logged.  
  854.             if (BitTst((Ptr)globals->fTraceMask, (long)diagID))
  855.             {
  856.                 // The trace point was set- check to see if the buffer exists and is ready.
  857.                 if (globals->fBufferEnabled)         // There is a buffer to write to
  858.                 {
  859.                     okLocked = UTLock(&(globals->fTraceLock)); // test and set flag
  860.             
  861.                     if (!okLocked) // If this trace request was locked out, then set locked-out flag
  862.                         globals->fLockedOutFlag = true;    // one tracing request interrupted another
  863.                     else
  864.                         HandleTraceData(globals, diagID, partCode, formatID, data1, data2, data3);
  865.                 }
  866.             
  867.             }
  868.         } 
  869.         
  870. //  Handle the breakpoint, if any.
  871.     if (BreakOnExit)
  872.     {
  873.         if (globals->fBreakOnceThenClear)
  874.         {
  875.             BitClr((Ptr)globals->fBreakMask,(long)diagID);
  876.             globals->fDebugMarkUnset = true;        // Signal cdev that debug mark was turned off
  877.             DebugStr("\pTracks User Breakpoint (Once)");
  878.         }
  879.         else
  880.             DebugStr("\pTracks User Breakpoint");
  881.     }
  882.     return;
  883. }
  884. // Dispatch incoming messages from control and status calls.
  885. pascal short TraceEntry(TraceGlobals *globals, short csCode, TraceParamBlock *paramPtr, DCtlPtr dCtl) 
  886.  {
  887. register short result;
  888. AutoTraceRec *atr;
  889.  
  890.     result = noErr;
  891.  
  892.     globals->fTraceLock = true;
  893.     
  894.     switch (csCode)
  895.         {
  896.         
  897.       case kSetTraceOnline:
  898.          
  899.         result = SetTraceOnline(globals, (char *)paramPtr);    /* The name of the target driver is in paramPtr (a pascal string) */
  900.         break;
  901.  
  902.       case kSetTraceOffline:
  903.         result = SetTraceOffline(globals);
  904.         break;
  905.  
  906.      /**** NO LONGER USED
  907.       case kTraceRead:
  908.         result = ReadTrace(globals, paramPtr);
  909.         break;
  910.      ****/
  911.  
  912.       case kAutoTrace:
  913.           atr = (AutoTraceRec *) paramPtr;
  914.         TraceProc((long)globals, atr->diagID, atr->partCode, atr->formatID, atr->data1, atr->data2, atr->data3);
  915.         
  916.         break;
  917.  
  918.         
  919.       case kEnableTraceBuffer:
  920.         result = EnableTraceBuffer(globals, paramPtr);
  921.         break;
  922.         
  923.       case kDisableTraceBuffer:
  924.         result = DisableTraceBuffer(globals);
  925.         break;
  926.  
  927.       case kSetTraceFileName:
  928.         result = SetTraceFileName(globals, paramPtr);
  929.         break;
  930.         
  931.       case kDisableTraceFile:
  932.         result = DisableTraceFile(globals);
  933.         break;
  934.         
  935.       case kWriteTraceBuffer:
  936.         result = WriteTraceBuffer(globals);
  937.         break;
  938.  
  939.       case kSetAutoWriteOn:
  940.           globals->fAutomaticWrite = true;
  941.         BitSet((Ptr) &dCtl->dCtlFlags, 2);        /* set bit 5/dNeedTime bit.IM I-471*/
  942.         break;
  943.  
  944.       case kSetAutoWriteOff:
  945.           globals->fAutomaticWrite = false;
  946.         BitClr((Ptr) &dCtl->dCtlFlags, 2);        /* Clear bit 5/dNeedTime bit. */
  947.         break;
  948.  
  949.       case kDoPeriodicTime:
  950.           if ( (globals->fBufferEnabled) && 
  951.              (globals->fFileEnabled) && 
  952.              (globals->fAutomaticWrite) )
  953.             result = WriteTraceBuffer(globals);
  954.         break;
  955.  
  956.       case kGetTraceStatus:
  957.         result = GetTraceStatus(globals, paramPtr);
  958.         break;
  959.  
  960.       case kGetTraceProc:
  961.         result = GetTraceProc(globals, paramPtr);
  962.         break;
  963.          
  964.         case    kSendCdevBreakMask:
  965.         case    kSendCdevTraceMask:
  966.         result = SendMaskToCdev(globals,paramPtr,csCode);
  967.             break;
  968.         case    kGetCdevBreakMask:
  969.         case    kGetCdevTraceMask:
  970.         result = GetMaskFromCDEV(globals,paramPtr,csCode);
  971.             break;
  972.         case    kResetEOF:        // Reset the Trace.outfile to zero bytes in length
  973.         result = ResetEOF(globals);
  974.             break;
  975.             
  976.         case    kClearTraceBuffer:        // Clear the buffer-
  977.         result = ClearTraceBuffer(globals);
  978.             break;
  979.             
  980.         case kStartedFromInit:
  981.             globals->fTraceOnStartup = (Boolean)paramPtr;
  982.             break;
  983.       default:
  984.         result = controlErr;        /* -17 */
  985.         break;
  986.         
  987.         }
  988.  
  989.     globals->fTraceLock = false;
  990.     return(result);
  991.     
  992. }
  993.  
  994. void HandleTraceData(TraceGlobals *globals, char diagID, char partCode, char formatID, long data1, long data2, long data3)
  995. {
  996. NewRecordTemplate template;
  997. register shex dataSize;
  998. shex recordSize;
  999. Ptr recordAddr;                    /* Address of new record */
  1000. shex splitSize;                    /* Size of first part of record */
  1001. Ptr secondAddr;                    /* Address of second part of record */
  1002. Ptr copyTo;
  1003. shex secondSize;
  1004. Boolean padByte;                /* true if pad byte is needed for even length */
  1005. short    procNameLen,callbyNameLen;
  1006. chex    LengthOfStackData;
  1007. Ptr        procNamePtr,callbyNamePtr,StackDataPtr;
  1008. char    SPBuffer[600];
  1009. SafeMemBlock    Mem;        // To append data onto a hunk of memory...
  1010. chex    temp;
  1011. register chex *bytePtr;
  1012. register Ptr    dataPtr;
  1013. register short err;
  1014.  
  1015.     // Set up Append Buffer--  
  1016.         
  1017.     Mem.Begin = SPBuffer;
  1018.     Mem.Mark = Mem.Begin;    // Start appending at the beginning
  1019.     Mem.End = Mem.Begin + sizeof(SPBuffer);
  1020.     // Determine size needed for new trace record
  1021.  
  1022.     switch (formatID)
  1023.         {
  1024.         
  1025.       case 0x01:    /* Stack Peek */                
  1026.         // Call of StackPeek--    We are at level 2 (level 1 is TraceProc... level 0 is calling function... )
  1027.         
  1028.         
  1029.         StackPeek(3,
  1030.           &procNamePtr, &procNameLen,
  1031.           &callbyNamePtr, &callbyNameLen,
  1032.           &StackDataPtr);
  1033.         
  1034.         // BlockAppend is like BlockMove (source,dest, numBytes) 
  1035.         // But it keeps track of the end of the buffer so it can Append (hence the name)
  1036.         
  1037.         
  1038.         // Stackpeek data is stored as follows:
  1039.         // stack data length byte (n) + stack data (n bytes)
  1040.         // Pascal string containing length of function name
  1041.         // Pascal string of previous caller...
  1042.             
  1043.         LengthOfStackData = 32;        // How much stack data to dump... this is for stackpeek
  1044.  
  1045.         BlockAppend((Ptr)&LengthOfStackData, &Mem, (long)1);        // Insert length byte
  1046.  
  1047.         BlockAppend(StackDataPtr,&Mem,(long) LengthOfStackData );    // Append stackdata
  1048.  
  1049.         temp = (chex) procNameLen;    // cast to char 
  1050.         BlockAppend( (Ptr) &temp,&Mem, 1L);        // Insert Length byte
  1051.         BlockAppend(procNamePtr,&Mem,(lhex)procNameLen);        // And the data
  1052.  
  1053.         temp = (chex) callbyNameLen;
  1054.         BlockAppend((Ptr)&temp, &Mem, 1L);         // Insert Length byte
  1055.         err = BlockAppend(callbyNamePtr, &Mem, (lhex)callbyNameLen);        // Insert the data from stackpeek
  1056.  
  1057.         dataSize = (shex) (Mem.Mark - Mem.Begin);
  1058.         dataPtr = (Ptr)Mem.Begin;
  1059.         break;
  1060.         
  1061.       case 0x02:    /* Ptr to memory */
  1062.         dataPtr = (Ptr)data1;
  1063.         dataSize = (shex)data2;
  1064.         break;
  1065.         
  1066.       case 0x03:    /* Pascal string */
  1067.         bytePtr = (chex *)data1;    // point to the pascal string length byte
  1068.           dataPtr = (Ptr) data1;
  1069.         dataSize = (shex) (*bytePtr) + 1; /* Pascal string length byte */
  1070.         break;
  1071.         
  1072.       case 0x04:    /* Long value */
  1073.         dataSize = 4;
  1074.         dataPtr = (Ptr) &data2; /* dummy up dataPtr */
  1075.         break;
  1076.  
  1077.       case 0x05:     /* pascal and long value */
  1078.  
  1079.         // data1 = Pascal String, data2 = long value, data3 ignored.
  1080.         BlockAppend((Ptr)&data2, &Mem, 4L);            // And the data    
  1081.  
  1082.           bytePtr = (chex *)data1;    // point to the pascal string length byte
  1083.         BlockAppend((Ptr)data1, &Mem, (long)(*bytePtr)+1);        // Insert the pstring
  1084.         
  1085.         dataSize = (shex) Mem.Mark - Mem.Begin;
  1086.         dataPtr = (Ptr)Mem.Begin;
  1087.           break;
  1088.         
  1089.       case 0x06:    // Length of data, Data, Pstr [data type name]
  1090.         // data1 = DataPtr, data2 = DataLen, data3 = Pstr
  1091.  
  1092.           BlockAppend((Ptr)&data2, &Mem, 4L);    // The length of data (a long)
  1093.  
  1094.         BlockAppend((Ptr)data1, &Mem, data2);        // Insert the data
  1095.         
  1096.         bytePtr = (chex *) data3;    // point to the pascal string length byte
  1097.         BlockAppend((Ptr)data3, &Mem, (long) *bytePtr+1);        // Insert the pstring
  1098.  
  1099.         dataSize = (shex) (Mem.Mark - Mem.Begin);
  1100.         dataPtr = (Ptr)Mem.Begin;      
  1101.           break;
  1102.         
  1103.         
  1104.     //  values 07 through 128 are reserved for Orion Network systems.  
  1105.     //  values 129 are free for public use.
  1106.     
  1107.       default:
  1108.           // DebugStr("\pTracks: Unknown Format ID");
  1109.         dataSize = 0;
  1110.         dataPtr = 0; // Should make this some kind of error record...
  1111.         break;
  1112.         
  1113.         }
  1114.     if (dataSize > 1024)
  1115.     {    
  1116.         dataSize = 1024; /* truncate to 1024 max */
  1117.     }
  1118.     recordSize = dataSize + sizeof(template);
  1119.     
  1120.     // Make sure that the size is even:
  1121.     
  1122.     if (recordSize & 1)  // If Odd
  1123.         {
  1124.         padByte = true;
  1125.         recordSize++;
  1126.         }
  1127.     else        // Its even...
  1128.         padByte = false;
  1129.     
  1130.     // Get a new record to copy the data into
  1131.     
  1132.     GetNewRecord(globals, recordSize, &recordAddr, &splitSize, &secondAddr);
  1133.      
  1134.     // Fill in the record template
  1135.     // (Fill in the timestamps after calling GetNewRecord() in case
  1136.     // a lost-data record was inserted by GetNewRecord() - this keeps
  1137.     // our timestamps later than its timestamps.)
  1138.     
  1139.     template.length = recordSize;
  1140.     template.diagID = diagID;
  1141.     template.partCode = partCode;
  1142.     template.timeStampType = globals->fTimeStampType;
  1143.     
  1144.     if (globals->fTimeStampType == 1)
  1145.         GetDateTime(&template.timeStamp); // Date and Time timestamp
  1146.     else
  1147.         template.timeStamp = TickCount(); // Ticks (1/60 sec) timestamp
  1148.     template.formatID = formatID;
  1149.  
  1150.     // Where TTTTTTTT is the template, and DDDDDDDDDDDD is the data, then
  1151.     // recordAddr, splitSize, and secondAddr represent one of these:
  1152.     //              <----------- BUFFER ----------->
  1153.     //         (1)        ----TTTTTTTTDDDDDDDDDDDD-------- 
  1154.     //         (2)        DDDDDDDDDDDD------------TTTTTTTT
  1155.     //         (3)        DDDDDDDD------------TTTTTTTTDDDD
  1156.     //         (4)        TTTTDDDDDDDDDDDD------------TTTT
  1157.     // In all cases, recordAddr points to the start of the template area.
  1158.     // In case (1), secondAddr is nil, and in cases (2), (3), and (4) secondAddr
  1159.     // points to the start of the buffer.
  1160.     // In case (1), splitSize is the size of the entire record.  In case (2),
  1161.     // splitSize is exactly the size of the template.  In case (3), splitSize is
  1162.     // larger than the template size but smaller than the record size.  In case
  1163.     // (4), splitSize is smaller than the template size.
  1164.     
  1165.     copyTo = recordAddr;
  1166.     if (copyTo != nil)
  1167.         {
  1168.         
  1169.         // First copy the template:
  1170.         
  1171.         if (splitSize < sizeof(template))
  1172.             {
  1173.             BlockMove((Ptr) &template, copyTo, splitSize);
  1174.             copyTo = secondAddr;
  1175.             secondAddr = nil;    // there is no longer a second part
  1176.             secondSize = sizeof(template) - splitSize;
  1177.             BlockMove((Ptr) &template, copyTo, secondSize);
  1178.             copyTo += secondSize;
  1179.             splitSize = dataSize;
  1180.             }
  1181.         else
  1182.             {
  1183.             BlockMove((Ptr) &template, copyTo, sizeof(template));
  1184.             copyTo += sizeof(template);
  1185.             splitSize -= sizeof(template);
  1186.             if (splitSize == 0)
  1187.                 {
  1188.                 copyTo = secondAddr;
  1189.                 secondAddr = nil;
  1190.                 splitSize = dataSize;
  1191.                 }
  1192.             }
  1193.         
  1194.         // Then copy the data:
  1195.         
  1196.         if (dataSize > 0)
  1197.             {
  1198.             if (splitSize < dataSize)
  1199.                 {
  1200.                 BlockMove(dataPtr, copyTo, splitSize);
  1201.                 copyTo = secondAddr;
  1202.                 secondSize = dataSize - splitSize;
  1203.                 BlockMove(dataPtr + splitSize, copyTo, secondSize);
  1204.                 copyTo += secondSize;
  1205.                 }
  1206.             else
  1207.                 {
  1208.                 BlockMove(dataPtr, copyTo, dataSize);
  1209.                 copyTo += dataSize;
  1210.                 }
  1211.                 
  1212.             // Clear the pad byte if there is one
  1213.             // (Since buffer size is always even, we are guaranteed not to write
  1214.             //  past the end of the buffer for this last byte since it is only
  1215.             //  written following an odd data length.)
  1216.             
  1217.             if (padByte)
  1218.                 *copyTo = 0;
  1219.             }
  1220.             
  1221.         }
  1222.         
  1223.     // Unlock the trace code...
  1224.     
  1225.     globals->fTraceLock = false;
  1226.         
  1227. }
  1228.  
  1229.  
  1230. snum WriteTraceBuffer(TraceGlobals *globals)
  1231.     {
  1232.     register snum result;
  1233.     long bytesToWrite;
  1234.     register Ptr nextRead;
  1235.     register Ptr nextWrite;
  1236.     register Ptr buffEnd;
  1237.     register Ptr buffStart;
  1238.     lhex firstPart;
  1239.     lhex secondPart;
  1240.     
  1241.     // If the buffer and trace file are both enabled, then open the file,
  1242.     // write the trace buffer, and close the file:
  1243.     
  1244.     if ( ! globals->fBufferEnabled)
  1245.         result = nsvErr; // "Specified volume doesn't exist"
  1246.     else if ( ! globals->fFileEnabled)
  1247.         result = volOffLinErr; // "Volume not on-line"
  1248.     else
  1249.         {
  1250.         // Move some globals into register variables for optimization:
  1251.         
  1252.         nextRead = globals->fNextReadPtr;
  1253.         nextWrite = globals->fNextWritePtr;
  1254.         buffEnd = globals->fBuffEndPtr;
  1255.         buffStart = globals->fBuffStartPtr;
  1256.  
  1257.         // When nextRead == nextWrite, the buffer is empty.  Otherwise,
  1258.         // when nextRead < nextWrite, the data is in one piece in the middle of
  1259.         // the buffer, and when nextRead > nextWrite the data is in two pieces
  1260.         // (like the cross-section of a donut), from nextRead to the end of the buffer,
  1261.         // then wrapping around from the beginning of the buffer to nextWrite.
  1262.  
  1263.         if (nextRead == nextWrite)
  1264.             result = noErr; // empty buffer - nothing to write
  1265.         else
  1266.             {
  1267.             
  1268.             // Open the file - return an error if file is not found
  1269.             // (EnableTraceFile should have already created it)
  1270.         
  1271.         //    result = FSOpen(globals->fTraceFileName, globals->fVRefNum, &refNum);
  1272.              /****&(globals->fFileRefNum));--- NO LONGER USED *****/
  1273.              
  1274.             result = OpenTraceFile(globals);
  1275.  
  1276.             if (result == noErr)
  1277.                 {
  1278.                 
  1279.                 
  1280.                 // Set the file position to the end of the file.
  1281.                 (void) SetFPos(globals->fFileRefNum, fsFromLEOF, 0);
  1282.  
  1283.                 if (nextRead < nextWrite)
  1284.                     {
  1285.                     firstPart = nextWrite - nextRead; // size of entire piece of data
  1286.                     secondPart = 0;
  1287.                     }
  1288.                 else
  1289.                     {
  1290.                     firstPart = (buffEnd - nextRead) + 1; // size of first part of data
  1291.                     secondPart = nextWrite - buffStart; // size of second part of data
  1292.                     }
  1293.     
  1294.                 // The first part always starts at nextRead...
  1295.                 
  1296.                 bytesToWrite = firstPart;
  1297.                 result = FSWrite(globals->fFileRefNum, &bytesToWrite, nextRead);
  1298.                 globals->fBytesWritten += bytesToWrite;
  1299.                 
  1300.                 // If there is a second part, it always starts at buffStart...
  1301.                 
  1302.                 if ((result == noErr) && (secondPart != 0))
  1303.                     {
  1304.                     bytesToWrite = secondPart;
  1305.                     result = FSWrite(globals->fFileRefNum, &bytesToWrite, buffStart);
  1306.                     globals->fBytesWritten += bytesToWrite;
  1307.                     }
  1308.         
  1309.                 if (result != noErr)
  1310.                     globals->fWriteErr = result; // always save last write error
  1311.                             
  1312.                 // Clear the buffer...
  1313.                 
  1314.                 globals->fNextReadPtr = nextWrite;
  1315.  
  1316.                 // Close the file and flush the volume:
  1317.                 (void) CloseTraceFile(globals);        // ignore errors here
  1318.                 
  1319.                 }
  1320.  
  1321.             }
  1322.         
  1323.         }
  1324.  
  1325.  
  1326.     return(result);
  1327.  
  1328.     }
  1329. /*
  1330.  * StackPeek.c
  1331.  *
  1332.  */
  1333.  
  1334.  
  1335.  
  1336. /* StackPeek seaches n levels deep into the stack to find the calling procedure
  1337. ** and its parameters.  Copyright 1990-1991 Orion Network Systems, All rights reserved.  
  1338. */
  1339.  
  1340. #define kJMPA0Instr    0x4ED0            /* MC68000 JMP(A0) instruction format */
  1341. #define kRTSInstr    0x4E75            /* MC68000 RTS instruction format */
  1342. #define kRTDInstr    0x4E74            /* MC68000 RTD instruction format (first word) */    
  1343.  
  1344. /* UTPeek is a function which returns the current value of register A6 */
  1345. pascal long *UTPeekA6();
  1346.  
  1347. void StackPeek(short level, Ptr *procNamePtrLoc, short *procNameLenLoc,
  1348.  Ptr *callbyNamePtrLoc, short *callbyNameLenLoc, Ptr *stackPtrLoc)
  1349.      {
  1350.     long *framePtr;                /* used to point into the stack */
  1351.     long *procFramePtr;            /* used to point into the stack */
  1352.     register long *stackedArgsPtr;        /* used to point into the stack */
  1353.     short *procInstrPtr;        /* used to point at program instructions */
  1354.     short *callbyInstrPtr;        /* used to point at program instructions */
  1355.     register unsigned char *procSymPtr;
  1356.     register unsigned char *callbySymPtr;
  1357.     register short procSymLen;
  1358.     register short callbySymLen;
  1359.     int i;
  1360.     
  1361.  
  1362.     /*
  1363.      * Register A6 contains the current frame pointer, which points into the
  1364.      * stack at the previous frame pointer which belongs to the calling function.
  1365.      */
  1366.  
  1367.     framePtr = UTPeekA6();
  1368.     procFramePtr = (long *) *framePtr;
  1369.  
  1370.  
  1371.     /*
  1372.      * When 'level' is greater than zero, we go back on the stack to find
  1373.      * the stack information for an earlier function (in the calling chain).
  1374.      * Follow the frame pointer chain backwards, until we get to framePtr
  1375.      * for the function which was CALLED by the desired target function,
  1376.      * and this will get us procFramePtr for the target function.
  1377.      */
  1378.     
  1379.     for (i = 0; i < level; i++)
  1380.         {
  1381.         framePtr = procFramePtr;
  1382.         procFramePtr = (long *) *framePtr;
  1383.         }
  1384.  
  1385.  
  1386.     /*
  1387.      * The frame pointer for the function which was called by the target
  1388.      * function points into the stack just above the return address which
  1389.      * returns INTO the target function.
  1390.      * The frame pointer which was used by the target function points
  1391.      * further down into the stack just above the return address of the
  1392.      * function which the target function was called by.  Just below this
  1393.      * return address (of our target's caller) are the arguments to the
  1394.      * target function (pushed by our target's caller).
  1395.      */
  1396.  
  1397.      procInstrPtr = (short *) *(framePtr + 1);
  1398.      callbyInstrPtr = (short *) *(procFramePtr + 1);
  1399.      stackedArgsPtr = procFramePtr;
  1400.  
  1401.  
  1402.     /*
  1403.      * With the two instruction pointers, scan the actual code instructions
  1404.      * to find the Macsbug symbol for the function (look for RTS, JMP(A0),
  1405.      * or RTD --- the Macsbug 6.1 documentation lists RTD as a possibility)...
  1406.      */
  1407.  
  1408.     while ( (*procInstrPtr != kRTSInstr) &&
  1409.             (*procInstrPtr != kJMPA0Instr) &&
  1410.             (*procInstrPtr != kRTDInstr) )
  1411.         procInstrPtr++;
  1412.         
  1413.     procSymPtr = (unsigned char *) (procInstrPtr + 1);
  1414.     procSymLen = *procSymPtr++ & 0x7f;
  1415.         
  1416.     if (procSymLen == 0)
  1417.         {
  1418.         /* when variable symbol length is greater than 0x1f */
  1419.         procSymLen = *procSymPtr++;
  1420.         }
  1421.     else if (procSymLen > 0x1f)
  1422.         {
  1423.         /* fixed length symbol if first byte in the range of 0x20 through 0x7f */
  1424.         /* (with or without high bit set) */
  1425.         if (*procSymPtr & 0x80)    /* 16 byte symbol if high bit set in 2nd byte */
  1426.             procSymLen = 16;
  1427.         else
  1428.             procSymLen = 8;
  1429.         procSymPtr--;
  1430.         }
  1431.  
  1432.  
  1433.     while ( (*callbyInstrPtr != kRTSInstr) &&
  1434.             (*callbyInstrPtr != kJMPA0Instr) &&
  1435.             (*callbyInstrPtr != kRTDInstr) )
  1436.         callbyInstrPtr++;
  1437.         
  1438.     callbySymPtr = (unsigned char *) (callbyInstrPtr + 1);
  1439.     callbySymLen = *callbySymPtr++ & 0x7f;
  1440.         
  1441.     if (callbySymLen == 0)
  1442.         {
  1443.         /* when variable symbol length is greater than 0x1f */
  1444.         callbySymLen = *callbySymPtr++;
  1445.         }
  1446.     else if (callbySymLen > 0x1f)
  1447.         {
  1448.         /* fixed length symbol if in the range of 0x20 through 0x7f */
  1449.         if (*callbySymPtr & 0x80)
  1450.             callbySymLen = 16;
  1451.         else
  1452.             callbySymLen = 8;
  1453.         callbySymPtr--;
  1454.         }
  1455.  
  1456.  
  1457.     /* return values */
  1458.  
  1459.     *procNamePtrLoc = (Ptr) procSymPtr;
  1460.     *procNameLenLoc = procSymLen;
  1461.     *callbyNamePtrLoc = (Ptr) callbySymPtr;
  1462.     *callbyNameLenLoc = callbySymLen;
  1463.     *stackPtrLoc = (Ptr) stackedArgsPtr;
  1464.     
  1465.     return;
  1466.     
  1467.     }
  1468.     
  1469.